package org.dromara.common.core.config;

import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.dromara.common.core.config.properties.ThreadPoolProperties;
import org.dromara.common.core.utils.Threads;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;


/**
 * @Author 刘武贵
 * @Date 2024/8/15 22:00
 * @Description 功能描述：线程池配置
 */
@Slf4j
@AutoConfiguration
// 启用配置属性
// 该注解用于激活配置属性类，使得可以在应用程序中使用指定的配置属性
// 在这里，指定的配置属性类是ThreadPoolProperties，用于配置线程池的相关属性
@EnableConfigurationProperties(ThreadPoolProperties.class)
public class ThreadPoolConfig {

    /**
     * 核心线程数 = cpu 核心数 + 1
     */
    private final int core = Runtime.getRuntime().availableProcessors() + 1;

    /**
     * 用于任务调度的执行器服务
     */
    private ScheduledExecutorService scheduledExecutorService;

    /**
     * 创建并配置一个线程池任务执行器
     *
     * 通过此方法，可以自定义线程池的属性，从而更好地控制异步任务的执行
     *
     * @param threadPoolProperties 线程池的配置属性，用于设置线程池的各种参数，如核心线程数、最大线程数等
     * @return 配置好的 ThreadPoolTaskExecutor 实例，用于执行异步任务
     */
    @Bean(name = "threadPoolTaskExecutor")
    // 当配置文件中thread-pool.enabled的值为true时，才启用线程池配置
    @ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor(ThreadPoolProperties threadPoolProperties) {
        // 根据核心线程数设置最大线程数
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(core);
        executor.setMaxPoolSize(core * 2);
        // 设置队列容量和保持时间
        executor.setQueueCapacity(threadPoolProperties.getQueueCapacity());
        executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds());
        // 设置拒绝策略为调用者运行策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }

    /**
     * 执行周期性或定时任务
     * 配置和返回一个ScheduledExecutorService任务调度执行器
     * 该执行器用于管理和执行定时任务
     *
     * @return ScheduledExecutorService 返回一个配置好的ScheduledExecutorService实例
     */
    @Bean(name = "scheduledExecutorService")
    protected ScheduledExecutorService scheduledExecutorService() {
        // 创建一个ScheduledThreadPoolExecutor实例，用于执行定时任务
        // 使用core作为核心线程数，使用BasicThreadFactory构建器定制线程工厂，
        // 设置线程名称模式为"schedule-pool-%d"，守护线程为true，并使用CallerRunsPolicy作为拒绝策略
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core,
            new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
            new ThreadPoolExecutor.CallerRunsPolicy()) {
            // 在任务执行之后进行额外的处理，无论任务是否成功完成
            /**
             * 在执行Runnable任务后进行的操作
             * 该方法被设计为protected级别，允许子类根据需要进行进一步的扩展或修改行为
             * 主要用途是捕获并处理任务执行过程中可能抛出的异常，确保异常能够被捕获并记录
             *
             * @param r 可运行的任务，该参数在执行完成后被传入，用于标识刚刚完成执行的可运行任务
             * @param t 异常对象，该参数在任务抛出异常时被传入，用于表示任务执行过程中发生的异常
             */
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                super.afterExecute(r, t);
                Threads.printException(r, t);
            }
        };
        // 将创建的ScheduledThreadPoolExecutor实例赋值给类成员变量scheduledExecutorService
        this.scheduledExecutorService = scheduledThreadPoolExecutor;
        return scheduledThreadPoolExecutor;
    }

    /**
     * 销毁事件
     * 在应用程序关闭时销毁任务线程池
     * 该方法使用@PreDestroy注解，表明它在应用程序上下文关闭前被调用
     * 主要用于确保所有的后台任务都被正确关闭，释放相关资源
     */
    @PreDestroy
    public void destroy() {
        try {
            log.info("====关闭后台任务任务线程池====");
            // 关闭并等待任务线程池终止
            Threads.shutdownAndAwaitTermination(scheduledExecutorService);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

}
